package stackexchange.analyzer.evaluator;

import java.util.Vector;

import stackexchange.User;

public class F1Score {

	private double f1value;
	private double mcc;
	private double precision;
	private double recall;
	private double accuracy;
	private double specificity;
	private Vector<User> allUsers;
	private Vector<User> analyzedExperts;
	private Vector<User> trueExperts;// TODO obv volledige user sets
	private double tp;
	private double fp;
	private double tn;
	private double fn;
	private double sensitivity;
	private double npv;

	public F1Score(Vector<User> allUsers, Vector<User> analyzedExperts,
			Vector<User> experts) {
		this.allUsers = allUsers;
		this.analyzedExperts = analyzedExperts;
		this.trueExperts = experts;
		this.tn = computeTrueNegative();
		this.fp = computeFalsePositive();
		this.tp = computeTruePositive();
		this.fn = computeFalseNegative();
		this.precision = computePrecision();
		this.recall = computeRecall();
		this.accuracy = computeAccuracy();
		this.mcc = computeMCC();
		this.f1value = 2 * ((precision * recall) / (precision + recall));
		this.specificity = computeSpecificity();
		this.sensitivity = computeSensitivity();
		this.npv = computeNPV();
	}

	private double computeNPV() {
		return tn / (tn + fn);
	}

	private double computeMCC() {
		// TODO Auto-generated method stub
		return ((tp * tn) - (fp * fn))
				/ Math.sqrt((tp + fp) * (tp + fn) * (tn * fp) * (tn * fn));
	}

	private boolean containsId(Vector<User> group, int id) {
		for (int i = 0; i < group.size(); i++) {
			if (group.get(i).getId() == id) {
				return true;
			}
		}
		return false;
	}

	public double computeTruePositive() {
		double truePositive = 0;
		for (int i = 0; i < allUsers.size(); i++) {
			if (containsId(trueExperts, allUsers.get(i).getId())
					&& containsId(analyzedExperts, allUsers.get(i).getId())) {
				truePositive++;
			}
		}
		return truePositive;
	}

	public double computeFalsePositive() {
		double falsePositive = 0;
		for (int i = 0; i < allUsers.size(); i++) {
			if (!containsId(trueExperts, allUsers.get(i).getId())
					&& containsId(analyzedExperts, allUsers.get(i).getId())) {
				falsePositive++;
			}
		}
		return falsePositive;
	}

	public double computeFalseNegative() {
		double falseNegative = 0;
		for (int i = 0; i < allUsers.size(); i++) {
			if (containsId(trueExperts, allUsers.get(i).getId())
					&& !containsId(analyzedExperts, allUsers.get(i).getId())) {
				falseNegative++;
			}
		}
		return falseNegative;
	}

	public double computeTrueNegative() {
		double falseNegative = 0;
		for (int i = 0; i < allUsers.size(); i++) {
			if (!containsId(trueExperts, allUsers.get(i).getId())
					&& !containsId(analyzedExperts, allUsers.get(i).getId())) {
				falseNegative++;
			}
		}
		return falseNegative;
	}

	public double computeSpecificity() {
		return tn / (fp + tn);
	}

	private double computeSensitivity() {
		return tp / (tp + fn);
	}

	public double computePrecision() {
		return tp / (tp + fp);
	}

	public double computeRecall() {
		return tp / (tp + fn);
	}

	private double computeAccuracy() {
		return (tp + tn) / (tp + tn + fp + fn);
	}

	public double getPrecision() {
		return precision;
	}

	public double getRecall() {
		return recall;
	}

	public double getF1Score() {
		return f1value;
	}

	public double getAccuracy() {
		return accuracy;
	}

	public String toString() {
		return
		// "Precision: " + precision +
		// "\nRecall: " + recall
		"\nF1-score: " + f1value + "\nSpecificity" + specificity
				+ "\nSensivity" + sensitivity + "\nNPV: " + npv
				+ "\nAccuracy: " + accuracy + "\nMcc: " + mcc;
	}

	public double getMcc() {
		return mcc;
	}

	// public class Precision {
	// private Vector<Double> precResults;
	// private int maxIndex;
	// private int minIndex;
	// private double maxValue;
	// private double minValue;
	// int startValue;
	// private double mean;
	//
	// public Precision(Vector<Double> precResults, int startValue) {
	// this.precResults = precResults;
	// this.startValue = startValue;
	// calcstatistics();
	// }
	//
	// private void calcstatistics() {
	// double sum = 0;
	// int maxI = 0;
	// double maxV = precResults.get(0);
	// int minI = 0;
	// double minV = precResults.get(0);
	// for (int i = 1; i < precResults.size(); i++) {
	// double cur = precResults.get(i);
	// sum = sum + cur;
	// if (cur > maxV) {
	// maxI = i;
	// maxV = cur;
	// }
	// if (cur < minV) {
	// minI = i;
	// minV = cur;
	// }
	// // System.out.println(i + ": " + precResults.get(i));
	// maxValue = precResults.get(maxI);
	// minValue = precResults.get(minI);
	// maxIndex = maxI + startValue;
	// minIndex = minI + startValue;
	// mean = sum/precResults.size();
	// }
	// }
	//
	// public void printResults() {
	// for (int i = 0; i < precResults.size(); i++) {
	// System.out.println(i+startValue + ": " + precResults.get(i));
	// }
	// }
	//
	// public Vector<Double> getResults() {
	// return precResults;
	// }
	//
	// public int getMaxIndex() {
	// return maxIndex;
	// }
	//
	// public int getMinIndex() {
	// return minIndex;
	// }
	//
	// public double getMean(){
	// return mean;
	// }
	//
	// public double getMaxValue() {
	// return maxValue;
	// }
	//
	// public double getMinValue() {
	// return minValue;
	// }
	//
	// }

	// private double getPrec(Vector<User> a, Vector<User> b, int limit) {
	// int precision = 0;
	// a = cutVector(a, limit);
	// b = cutVector(b, limit);
	// for (int i = 0; i < limit; i++) {
	// if (containsId(a, b.get(i).getId())) {
	// precision++;
	// }
	// }
	// return (double) precision / limit;
	// }
	//
	// private Vector<User> cutVector(Vector<User> aVector, int limit) {
	// Vector<User> result = new Vector<User>();
	// for (int i = 0; i < limit; i++) {
	// result.add(aVector.get(i));
	// }
	// return result;
	// }
	//
	// public void printPrecs() {
	// int startValue = 5;
	// Vector<Double> results = new Vector<Double>();
	// for (int i = startValue; i < 100; i++) {
	// double prec = getPrec(analyzedExperts, trueExperts, i);
	// results.add(prec);
	// }
	// Precision prec = new Precision(results, startValue);
	// System.out.println("Max:" + prec.getMaxIndex() + ": " +
	// prec.getMaxValue());
	// System.out.println("Min:" + prec.getMinIndex() + ": " +
	// prec.getMinValue());
	// System.out.println("MEAN:" + prec.getMean());
	// // prec.printResults();
	// }
}
